#include <sys/select.h>
#include <stdio.h>
#include <unistd.h>
#include "dbm/dbus-monitor.h"

#define DBUS_INF "org.freedesktop.DBus"
#define DBUS_PATH "/org/freedesktop/DBus"
#define DBUS_SRV DBUS_INF

#define NET_DBUS_INF "org.freedesktop.NetworkManager"
#define NET_DBUS_PATH "/org/freedesktop/NetworkManager"

static fd_set rfds, wfds, efds;
static int mfd = 0;
static dbus_bool_t add_watch(DBusWatch *w, void *data);
static void remove_watch(DBusWatch *w, void *data);
static void dispatch(DBusMonitor *m);
//static void dbmEH (void *usr_data); //DBusMonitor Execption Handler




int main(int argc, char *argv[]) {
    char rule[DBM_MAX_RULE];
    const char *rfmt = "path=%s, interface=%s, type=%s, member=%s";
    FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds);
    mfd = 0;
    DBusMonitor *m = dbmCreateWithType(DBUS_BUS_SYSTEM, add_watch,
                                       remove_watch, dispatch);
    dbmInitConnection(m);
    snprintf(rule, DBM_MAX_RULE, rfmt, NET_DBUS_PATH, NET_DBUS_INF, "signal",
            "PropertiesChanged");
    dbmAddRule(m, rule);
    dbmAddHandler(m, dbmDefaultHandler, m, NULL);
    dbmStart(m);
    sleep(10);
    dbmStop(m);
    dbmDestroy(m);
    return 0;
   
}

dbus_bool_t add_watch(DBusWatch *w, void *data) {
    printf("calling %s watch=%p, enabled:%d\n", __func__, w, dbus_watch_get_enabled(w));
    if (!dbus_watch_get_enabled(w))
        return TRUE;
    DBusMonitor *d = (DBusMonitor*)data;
    dbmAddDBW(d, w);
    int fd = dbus_watch_get_unix_fd(w);
    if(fd>mfd) mfd = fd;
    FD_SET(fd, &efds);
    unsigned int flags = dbus_watch_get_flags(w);
    if(flags & DBUS_WATCH_READABLE) {
        FD_SET(fd, &rfds);
    }
    if(flags & DBUS_WATCH_WRITABLE) {
        FD_SET(fd, &wfds);
    }
    printf("added dbus watch fd=%d watch=%p, flags:%d\n", fd, w, flags);
    return TRUE;
}

void remove_watch(DBusWatch *w, void *data) {
    printf("calling %s watch=%p, enabled:%d\n", __func__, w, dbus_watch_get_enabled(w));
    DBusMonitor *d = (DBusMonitor*)data;
    if(dbmRemoveDBW(d, w)) { //避免重复remove
        int fd = dbus_watch_get_unix_fd(w);
        FD_CLR(fd, &rfds);
        FD_CLR(fd, &wfds);
        FD_CLR(fd, &efds);
        if(fd == mfd) mfd--;
        printf("removed dbus watch watch=%p, fd:%d\n", w, fd);
    }
}

void dispatch(DBusMonitor *m) {
    fd_set trfds, twfds, tefds;
    int nfds;
    int i;
    int fd;
    int cfd = dbmGetCtrlFd(m); //ctrl fd
    
    if(cfd > mfd) mfd = cfd;
    FD_SET(cfd, &rfds);
    FD_SET(cfd, &efds);
    do {
        trfds = rfds; twfds = wfds; tefds = efds;
        nfds = select(mfd+1, &trfds, &twfds, &tefds, NULL);
        if(nfds <= 0) {
            perror("DBusMonitor mainloop execption");
            break;
        }
        if(FD_ISSET(cfd, &tefds)) {
            fprintf(stderr, "DBusMonitor control pipe broken!\n");
            break;
        }
        if(FD_ISSET(cfd, &trfds)) {
            DBMCtrl cmd = -1;
            read(cfd, &cmd, sizeof(DBMCtrl));
            if(cmd == DBMSTOP) {
                break;
            }else {
                fprintf(stderr, "Unsupported DBusMonitor control cmd: %d!\n",
                        cmd);
            }
        }
        for(i=0; (fd=dbmGetWFd(m, i))>=0; i++) {
            if(FD_ISSET(fd, &trfds)) {
                dbmHandleDBusRead(m, fd);        
            }
            if(FD_ISSET(fd, &twfds)) {
                dbmHandleDBusWrite(m, fd);        
            }
            if(FD_ISSET(fd, &tefds)) {
                dbmHandleDBusExecption(m, fd);        
            }
        }
        
    }while(1);
    dbmCloseConnection(m);
}


